/********************************************
*
*>程序名称：贪吃蛇
*>成员1： 姓名：刘福山    学号：24039100297
*>成员2： 姓名：罗逸杰    学号：24039100704
*
********************************************/


#include <windows.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>

#define N 20

void ClearScreen();
int GetKey(int n, int Key);
void LRecoedPrint(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer);
void BRecoedPrint(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer);
void PausePrint(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer);
int PauseLogic(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer);
void EndPrint(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer);
void EndLogic(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer);
void GamePrint(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer);
void GameLogic(char Map[N][N], int* HighestScore, HANDLE hBuffer);
void HScorePrint(char Map[N][N], int* HighestScore, HANDLE hBuffer);
void HScoreLogic(char Map[N][N], int* HighestScore, HANDLE hBuffer);
void SelecPrint(char Map[N][N], HANDLE hBuffer);
void SelecLogic(char Map[N][N], int* HighestScore, HANDLE hBuffer);
void BeginPrint(char Map[N][N], HANDLE hBuffer);
void BeginLogic(char Map[N][N], HANDLE hBuffer);
void HelpPrint();
void StartPrint();
void ExplainPrint();

int main()
{
	//获取最高分
    int HighestScore;
    //最高分存储规则：
	// - 文件保存在程序同级目录
	// - 纯文本格式，只包含一个数字
	// - 如果文件损坏，自动重置为0分
	FILE* file = fopen("Hignest Score.txt", "r");
    //如果保存最高分的文件丢失，则重新创建一个文件
    if(file == NULL)
	{
		//创建新文件，将最高分设置为0
        printf("游戏文件丢失，正在创建新文件···");
        FILE* file1 = fopen("Hignest Score.txt", "w");
        HighestScore = 0;
        fprintf(file1, "%d", HighestScore);
		fclose(file1);
		Sleep(1500);
		//清屏后进入下一步
		ClearScreen();
    }
    else
    {
		//从文件读取数字
		fscanf(file, "%d", &HighestScore);
		fclose(file);	
	}

	//游戏前说明，提示一些必要的说明
	ExplainPrint();
	ClearScreen();
	
	//开始界面的选择，主要为开始和帮助
	//帮助函数嵌套在开始界面中
	StartPrint();

	//清屏后进入游戏本体
	ClearScreen();
	Sleep(500);
	
	//定义双缓冲的句柄
	HANDLE hBuffer = CreateConsoleScreenBuffer(
	    GENERIC_READ | GENERIC_WRITE,	//缓冲区设置为可读（输出）可写（改变状态）
	    FILE_SHARE_READ | FILE_SHARE_WRITE,
	    NULL,	//默认安全属性
	    CONSOLE_TEXTMODE_BUFFER,	//应为程序由字符呈现，故采用文本模式的缓冲区
	    NULL	//无模板
	);
	SetConsoleActiveScreenBuffer(hBuffer); 		//设置为活动缓冲区
	
    //定义并清洗地图
    char Map[N][N];
    for(int i = 0; i < N; i++)
	{
		for(int j = 0; j < N; j++)
		{
			Map[i][j] = ' ';
		}
	}
    
    //开始界面，简单的加载动画
    BeginLogic(Map, hBuffer);
    
    //选择界面，选项所对应的函数都被嵌套在选择界面的函数中
    //将地图，缓冲区，以及最高分的内存地址都传递到函数中
    SelecLogic(Map, &HighestScore, hBuffer);
    
    //释放资源
    SetConsoleActiveScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE));
	CloseHandle(hBuffer);
	
    printf("游戏结束\n");
    return 0;
}







void ClearScreen()
{
	//清屏函数定义
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coordScreen = {0, 0};
    DWORD cCharsWritten;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    DWORD dwConSize;

    // 获取控制台缓冲区大小
    GetConsoleScreenBufferInfo(hConsole, &csbi);
    dwConSize = csbi.dwSize.X * csbi.dwSize.Y;

    // 用空格填充整个屏幕
    FillConsoleOutputCharacter(hConsole, ' ', dwConSize, coordScreen, &cCharsWritten);

    // 重置光标位置到左上角
    SetConsoleCursorPosition(hConsole, coordScreen);
}

int GetKey(int n, int Key)
{
	//n表示循环次数（在函数外表现为速度数值），即总停顿多长时间，Key为返回的键盘输入
	//每次循环15毫秒，速度越大，循环次数越多，每次行动停顿时间越长
	//每15毫秒录取一次键盘输入可以尽可能避免按键不生效的情况
	//Key在函数外面表示为方向，将方向传递到函数内可以保证没有输入方向时可以返回原方向
    int sign = 0;
    //输入映射：
	//1=上(W) 2=左(A) 3=下(S) 4=右(D) 
	//5=F1(加速) 6=F2(减速) 0=ESC(退出)
    for(int i = 0; i < n; i++)
    {
    	//sign为0说明没有录取到键盘输入，sign为1时键盘已经录入，不需要再次录入，但要继续循环
        if(sign == 0)
        {
            if (GetAsyncKeyState('W') & 0x8000)
		    {
		    	//此处是为了识别反方向操作，录入相反方向时，将会跳出键盘录入过程，不记录此次键盘录入
		    	//由于单次键盘录入为多个if的判断嵌套，continue第一次测试时出现异常，因此使用goto跳出if嵌套
		    	if(Key == 3)
					goto a;
                Key = 1;
                sign = 1;
            }
            if (GetAsyncKeyState('A') & 0x8000)
		    {
		    	if(Key == 4)
					goto a;
                Key = 2;
                sign = 1;
            }
            if (GetAsyncKeyState('S') & 0x8000)
		    {
		    	if(Key == 1)
					goto a;
                Key = 3;
                sign = 1;
            }
            if (GetAsyncKeyState('D') & 0x8000)
		    {
		    	if(Key == 2)
					goto a;
		        Key = 4;
                sign = 1;
		    }
		    if (GetAsyncKeyState(VK_F1) & 0x8000)
		    {
		        Key = 5;
                sign = 1;
		    }
			if (GetAsyncKeyState(VK_F2) & 0x8000)
		    {
		        Key = 6;
                sign = 1;
		    }
            
        }
        //录入ESC时直接退出，不需要等待循环结束
        if (GetAsyncKeyState(VK_ESCAPE) & 0x8000)
	    {
	        return 0;
	    }a:;
        Sleep(15); //暂停15毫秒
    }
    return Key;
}

void LRecoedPrint(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer)
{
    //定义 CHAR_INFO 缓冲区（存储字符 + 颜色）
    CHAR_INFO charBuffer[N + 2][2*N + 2];

    //填充 CHAR_INFO 缓冲区
    //颜色内容：边框：绿色
    //          背景：白色
    //          蛇：黄色
    //          食物：红色
    //得分显示
    //接近记录弹窗
    char scoreStr[40];
	sprintf(scoreStr, "Score: %d      HighestScore: %d", score, *HighestScore);
    for(int i = 0; i < 2*N+2; i++)
	{
		if(i <= (int)strlen(scoreStr) && i > 0)
			charBuffer[0][i].Char.AsciiChar = scoreStr[i-1];
		else
			charBuffer[0][i].Char.AsciiChar = ' ';
		charBuffer[0][i].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
	}
    //上边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[1][i].Char.AsciiChar = '-';
		charBuffer[1][i].Attributes = FOREGROUND_GREEN;
	}
	//地图内容
    for (int i = 0; i < N-1; i++)
	{
        //左边界
        charBuffer[i+2][0].Char.AsciiChar = '|';
        charBuffer[i+2][0].Attributes = FOREGROUND_GREEN;

        //地图内容
        for(int j = 0; j < N; j++)
		{
            charBuffer[i+2][2*j+1].Char.AsciiChar = Map[i][j];
            if (Map[i][j] == '@' || Map[i][j] == '*')
                charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else if (Map[i][j] == 'O')
                charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else
            	charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            charBuffer[i+2][2*j+2].Char.AsciiChar = ' ';
            charBuffer[i+2][2*j+2].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
			                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
        }
        //接近记录弹窗
        char Str[30];
        //弹窗上边界
        if(i == 9)
        {
            sprintf(Str, "---------------------");
            for(int j = 9,k = 0; j < 31; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        //弹窗内容
        if(i == 10)
        {
            sprintf(Str, "|    What a pity!   |");
            for(int j = 9,k = 0; j < 31; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        if(i == 11)
        {
            sprintf(Str, "|%3d short of record|", *HighestScore - score);
            for(int j = 9,k = 0; j < 31; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        //弹窗下边界
        if(i == 12)
        {
            sprintf(Str, "---------------------");
            for(int j = 9,k = 0; j < 31; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        //右边界
        charBuffer[i+2][2*N+1].Char.AsciiChar = '|';
		charBuffer[i+2][2*N+1].Attributes = FOREGROUND_GREEN;
    }
    //下边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[N+1][i].Char.AsciiChar = '-';
		charBuffer[N+1][i].Attributes = FOREGROUND_GREEN;
	}

    COORD bufferSize = {2*N + 2, N + 2};    //定义写入区域
    COORD bufferCoord = {0, 0};      //从 CHAR_INFO 缓冲区的 (0,0) 开始读取
    SMALL_RECT writeRegion = {0, 0, 2*N + 1, N + 1};   //控制台写入区域

    //使用双缓冲一次性写入字符和颜色
    WriteConsoleOutput(
        hBuffer,    //控制台的屏幕缓冲区（直接输出的缓冲区）
        (CHAR_INFO*)charBuffer,     // CHAR_INFO 缓冲区（预先准备的写入了字符和颜色的缓冲区）
        bufferSize,    //写入的区域
        bufferCoord,    //读取起始点
        &writeRegion    //控制台写入区域
    );

    //重置光标
    SetConsoleCursorPosition(hBuffer, (COORD){0, 0});
    
    Sleep(2000);
}

void BRecoedPrint(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer)
{
    //定义 CHAR_INFO 缓冲区（存储字符 + 颜色）
    CHAR_INFO charBuffer[N + 2][2*N + 2];

    //填充 CHAR_INFO 缓冲区
    //颜色内容：边框：绿色
    //          背景：白色
    //          蛇：黄色
    //          食物：红色
    //得分显示
    
    //将最高分保存到文件
    FILE* file = fopen("Hignest Score.txt", "w");
	fprintf(file, "%d", *HighestScore);
	fclose(file);
    
    //破纪录弹窗
    char scoreStr[40];
	sprintf(scoreStr, "Score: %d      HighestScore: %d", score, *HighestScore);
    for(int i = 0; i < 2*N+2; i++)
	{
		if(i <= (int)strlen(scoreStr) && i > 0)
			charBuffer[0][i].Char.AsciiChar = scoreStr[i-1];
		else
			charBuffer[0][i].Char.AsciiChar = ' ';
		charBuffer[0][i].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
	}
    //上边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[1][i].Char.AsciiChar = '-';
		charBuffer[1][i].Attributes = FOREGROUND_GREEN;
	}
	//地图内容
    for (int i = 0; i < N-1; i++)
	{
        //左边界
        charBuffer[i+2][0].Char.AsciiChar = '|';
        charBuffer[i+2][0].Attributes = FOREGROUND_GREEN;

        //地图内容
        for(int j = 0; j < N; j++)
		{
            charBuffer[i+2][2*j+1].Char.AsciiChar = Map[i][j];
            if (Map[i][j] == '@' || Map[i][j] == '*')
                charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else if (Map[i][j] == 'O')
                charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else
            	charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            charBuffer[i+2][2*j+2].Char.AsciiChar = ' ';
            charBuffer[i+2][2*j+2].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
			                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
        }
        //破纪录弹窗
        char Str[30];
        //弹窗上边界
        if(i == 9)
        {
            sprintf(Str, "-------------------");
            for(int j = 10,k = 0; j < 30; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        //弹窗内容
        if(i == 10)
        {
            sprintf(Str, "|   New record!   |");
            for(int j = 10,k = 0; j < 30; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        if(i == 11)
        {
            sprintf(Str, "|      %4d       |", *HighestScore);
            for(int j = 10,k = 0; j < 30; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        //弹窗下边界
        if(i == 12)
        {
            sprintf(Str, "-------------------");
            for(int j = 10,k = 0; j < 30; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        //右边界
        charBuffer[i+2][2*N+1].Char.AsciiChar = '|';
		charBuffer[i+2][2*N+1].Attributes = FOREGROUND_GREEN;
    }
    //下边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[N+1][i].Char.AsciiChar = '-';
		charBuffer[N+1][i].Attributes = FOREGROUND_GREEN;
	}

    COORD bufferSize = {2*N + 2, N + 2};    //定义写入区域
    COORD bufferCoord = {0, 0};      //从 CHAR_INFO 缓冲区的 (0,0) 开始读取
    SMALL_RECT writeRegion = {0, 0, 2*N + 1, N + 1};   //控制台写入区域

    //使用双缓冲一次性写入字符和颜色
    WriteConsoleOutput(
        hBuffer,    //控制台的屏幕缓冲区（直接输出的缓冲区）
        (CHAR_INFO*)charBuffer,     // CHAR_INFO 缓冲区（预先准备的写入了字符和颜色的缓冲区）
        bufferSize,    //写入的区域
        bufferCoord,    //读取起始点
        &writeRegion    //控制台写入区域
    );

    //重置光标
    SetConsoleCursorPosition(hBuffer, (COORD){0, 0});
    
    Sleep(2000);
}

void PausePrint(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer)
{
    //定义 CHAR_INFO 缓冲区（存储字符 + 颜色）
    CHAR_INFO charBuffer[N + 2][2*N + 2];

    //填充 CHAR_INFO 缓冲区
    //颜色内容：边框：绿色
    //          背景：白色
    //          蛇：黄色
    //          食物：红色
    //得分显示
    //游戏暂停弹窗
    char scoreStr[40];
	sprintf(scoreStr, "Score: %d      HighestScore: %d", score, *HighestScore);
    for(int i = 0; i < 2*N+2; i++)
	{
		if(i <= (int)strlen(scoreStr) && i > 0)
			charBuffer[0][i].Char.AsciiChar = scoreStr[i-1];
		else
			charBuffer[0][i].Char.AsciiChar = ' ';
		charBuffer[0][i].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
	}
    //上边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[1][i].Char.AsciiChar = '-';
		charBuffer[1][i].Attributes = FOREGROUND_GREEN;
	}
	//地图内容
    for (int i = 0; i < N-1; i++)
	{
        //左边界
        charBuffer[i+2][0].Char.AsciiChar = '|';
        charBuffer[i+2][0].Attributes = FOREGROUND_GREEN;

        //地图内容
        for(int j = 0; j < N; j++)
		{
            charBuffer[i+2][2*j+1].Char.AsciiChar = Map[i][j];
            if (Map[i][j] == '@' || Map[i][j] == '*')
                charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else if (Map[i][j] == 'O')
                charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else
            	charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            charBuffer[i+2][2*j+2].Char.AsciiChar = ' ';
            charBuffer[i+2][2*j+2].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
			                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
        }
        //结束弹窗
        char Str[30];
        //弹窗上边界
        if(i == 9)
        {
            sprintf(Str, "-------------------");
            for(int j = 10,k = 0; j < 30; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        //弹窗内容
        if(i == 10)
        {
            sprintf(Str, "|    Game Pause.  |");
            for(int j = 10,k = 0; j < 30; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        if(i == 11)
        {
            sprintf(Str, "|1.contiune 2.over|");
            for(int j = 10,k = 0; j < 30; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        //弹窗下边界
        if(i == 12)
        {
            sprintf(Str, "-------------------");
            for(int j = 10,k = 0; j < 30; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        //右边界
        charBuffer[i+2][2*N+1].Char.AsciiChar = '|';
		charBuffer[i+2][2*N+1].Attributes = FOREGROUND_GREEN;
    }
    //下边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[N+1][i].Char.AsciiChar = '-';
		charBuffer[N+1][i].Attributes = FOREGROUND_GREEN;
	}

    COORD bufferSize = {2*N + 2, N + 2};    //定义写入区域
    COORD bufferCoord = {0, 0};      //从 CHAR_INFO 缓冲区的 (0,0) 开始读取
    SMALL_RECT writeRegion = {0, 0, 2*N + 1, N + 1};   //控制台写入区域

    //使用双缓冲一次性写入字符和颜色
    WriteConsoleOutput(
        hBuffer,    //控制台的屏幕缓冲区（直接输出的缓冲区）
        (CHAR_INFO*)charBuffer,     // CHAR_INFO 缓冲区（预先准备的写入了字符和颜色的缓冲区）
        bufferSize,    //写入的区域
        bufferCoord,    //读取起始点
        &writeRegion    //控制台写入区域
    );

    //重置光标
    SetConsoleCursorPosition(hBuffer, (COORD){0, 0});
}

int PauseLogic(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer)
{
    //打印地图
	PausePrint(Map, HighestScore, score, hBuffer);
	Sleep(500);
    while (1)
	{
        //检测键盘录入
		if(GetAsyncKeyState('1') & 0x8000)
		{
			return 1;
		}
		if(GetAsyncKeyState('2') & 0x8000)
		{
			return 2;
		}
		
        PausePrint(Map, HighestScore, score, hBuffer);
        Sleep(100);
    }
}

void EndPrint(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer)
{
    //定义 CHAR_INFO 缓冲区（存储字符 + 颜色）
    CHAR_INFO charBuffer[N + 2][2*N + 2];

    //填充 CHAR_INFO 缓冲区
    //颜色内容：边框：绿色
    //          背景：白色
    //          蛇：黄色
    //          食物：红色
    //得分显示
    //游戏结束弹窗
    char scoreStr[40];
	sprintf(scoreStr, "Score: %d      HighestScore: %d", score, *HighestScore);
    for(int i = 0; i < 2*N+2; i++)
	{
		if(i <= (int)strlen(scoreStr) && i > 0)
			charBuffer[0][i].Char.AsciiChar = scoreStr[i-1];
		else
			charBuffer[0][i].Char.AsciiChar = ' ';
		charBuffer[0][i].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
	}
    //上边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[1][i].Char.AsciiChar = '-';
		charBuffer[1][i].Attributes = FOREGROUND_GREEN;
	}
	//地图内容
    for (int i = 0; i < N-1; i++)
	{
        //左边界
        charBuffer[i+2][0].Char.AsciiChar = '|';
        charBuffer[i+2][0].Attributes = FOREGROUND_GREEN;

        //地图内容
        for(int j = 0; j < N; j++)
		{
            charBuffer[i+2][2*j+1].Char.AsciiChar = Map[i][j];
            if (Map[i][j] == '@' || Map[i][j] == '*')
                charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else if (Map[i][j] == 'O')
                charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else
            	charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            charBuffer[i+2][2*j+2].Char.AsciiChar = ' ';
            charBuffer[i+2][2*j+2].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
			                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
        }
        //结束弹窗
        char Str[30];
        //弹窗上边界
        if(i == 9)
        {
            sprintf(Str, "-------------------");
            for(int j = 10,k = 0; j < 30; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        //弹窗内容
        if(i == 10)
        {
            sprintf(Str, "|    Game Over!   |");
            for(int j = 10,k = 0; j < 30; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        if(i == 11)
        {
            sprintf(Str, "| 1.again  2.exit |");
            for(int j = 10,k = 0; j < 30; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        //弹窗下边界
        if(i == 12)
        {
            sprintf(Str, "-------------------");
            for(int j = 10,k = 0; j < 30; j++)
            {
                charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
            }
        }
        //右边界
        charBuffer[i+2][2*N+1].Char.AsciiChar = '|';
		charBuffer[i+2][2*N+1].Attributes = FOREGROUND_GREEN;
    }
    //下边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[N+1][i].Char.AsciiChar = '-';
		charBuffer[N+1][i].Attributes = FOREGROUND_GREEN;
	}

    COORD bufferSize = {2*N + 2, N + 2};    //定义写入区域
    COORD bufferCoord = {0, 0};      //从 CHAR_INFO 缓冲区的 (0,0) 开始读取
    SMALL_RECT writeRegion = {0, 0, 2*N + 1, N + 1};   //控制台写入区域

    //使用双缓冲一次性写入字符和颜色
    WriteConsoleOutput(
        hBuffer,    //控制台的屏幕缓冲区（直接输出的缓冲区）
        (CHAR_INFO*)charBuffer,     // CHAR_INFO 缓冲区（预先准备的写入了字符和颜色的缓冲区）
        bufferSize,    //写入的区域
        bufferCoord,    //读取起始点
        &writeRegion    //控制台写入区域
    );

    //重置光标
    SetConsoleCursorPosition(hBuffer, (COORD){0, 0});
}

void EndLogic(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer)
{
	//结束函数前先对分数进行一定的判定
	//如果达到最高分，说明突破或与最高分持平，进入突破记录界面
	if(*HighestScore == score)
		BRecoedPrint(Map, HighestScore, score, hBuffer);
	//如果未达到最高分，但差的分数少于30分，进入鼓励界面
	else if(*HighestScore - score < 30)
		LRecoedPrint(Map, HighestScore, score, hBuffer);
	
	//除去上述两种情况的其他情况无特殊界面
	//上述两个函数内部有停留2秒代码，之后会进入结束界面
	
    //打印地图
	EndPrint(Map, HighestScore, score, hBuffer);
	Sleep(500);
    while (1)
	{
        //检测键盘录入
		if(GetAsyncKeyState('1') & 0x8000)
		{
			//重新开始，递归调用游戏函数，游戏函数结束后退出循环，结束函数同样结束
			//（游戏函数结束必须经由退出函数，因此游戏函数结束意味着退出函数同意结束）
			GameLogic(Map, HighestScore, hBuffer);
			break;
		}
		if(GetAsyncKeyState('2') & 0x8000)
		{
			//直接退出
			break;
		}
        EndPrint(Map, HighestScore, score, hBuffer);
        Sleep(100);
    }
}

void GamePrint(char Map[N-1][N], int* HighestScore, int score, HANDLE hBuffer)
{
    //定义 CHAR_INFO 缓冲区（存储字符 + 颜色）
    CHAR_INFO charBuffer[N + 2][2*N + 2];

    //填充 CHAR_INFO 缓冲区
    //颜色内容：边框：绿色
    //          背景：白色
    //          蛇：黄色
    //          食物：红色
    //得分显示
    char scoreStr[40];
	sprintf(scoreStr, "Score: %d      HighestScore: %d", score, *HighestScore);
    for(int i = 0; i < 2*N+2; i++)
	{
		if(i <= (int)strlen(scoreStr) && i > 0)
			charBuffer[0][i].Char.AsciiChar = scoreStr[i-1];
		else
			charBuffer[0][i].Char.AsciiChar = ' ';
		charBuffer[0][i].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
	}
    //上边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[1][i].Char.AsciiChar = '-';
		charBuffer[1][i].Attributes = FOREGROUND_GREEN;
	}
	//地图内容
    for (int i = 0; i < N-1; i++)
	{
        //左边界
        charBuffer[i+2][0].Char.AsciiChar = '|';
        charBuffer[i+2][0].Attributes = FOREGROUND_GREEN;

        //地图内容
        for(int j = 0; j < N; j++)
		{
            charBuffer[i+2][2*j+1].Char.AsciiChar = Map[i][j];
            if (Map[i][j] == '@' || Map[i][j] == '*')
                charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else if (Map[i][j] == 'O')
                charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else
            	charBuffer[i+2][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            charBuffer[i+2][2*j+2].Char.AsciiChar = ' ';
            charBuffer[i+2][2*j+2].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
			                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
        }
        //右边界
        charBuffer[i+2][2*N+1].Char.AsciiChar = '|';
		charBuffer[i+2][2*N+1].Attributes = FOREGROUND_GREEN;
    }
    //下边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[N+1][i].Char.AsciiChar = '-';
		charBuffer[N+1][i].Attributes = FOREGROUND_GREEN;
	}

    COORD bufferSize = {2*N + 2, N + 2};    //定义写入区域
    COORD bufferCoord = {0, 0};      //从 CHAR_INFO 缓冲区的 (0,0) 开始读取
    SMALL_RECT writeRegion = {0, 0, 2*N + 1, N + 1};   //控制台写入区域

    //使用双缓冲一次性写入字符和颜色
    WriteConsoleOutput(
        hBuffer,    //控制台的屏幕缓冲区（直接输出的缓冲区）
        (CHAR_INFO*)charBuffer,     // CHAR_INFO 缓冲区（预先准备的写入了字符和颜色的缓冲区）
        bufferSize,    //写入的区域
        bufferCoord,    //读取起始点
        &writeRegion    //控制台写入区域
    );

    //重置光标
    SetConsoleCursorPosition(hBuffer, (COORD){0, 0});
}

void GameLogic(char Map[N][N], int* HighestScore, HANDLE hBuffer)
{
    //清洗地图
    for(int i = 0; i < N-1; i++)
	{
		for(int j = 0; j < N; j++)
		{
			Map[i][j] = ' ';
		}
	}
    //定义蛇的长度及所有部位的坐标
    int length = 3, snake[N*N][2];
	//定义初始蛇各个部位的坐标
    snake[0][0] = N/2; snake[0][1] = N/2;
    snake[1][0] = N/2 + 1; snake[1][1] = N/2;
    snake[2][0] = N/2 + 2; snake[2][1] = N/2;
    //定义食物的坐标,初始方向（向上），初始分数（0）以及初始速度（15）
    int x, y, direction = 1, score = 0, speed = 15;
    //随机生成食物，并确保食物的生成合法
    srand(time(NULL));
    while(1)
	{
		x = rand() % (N-1);
		y = rand() % N;
		if(Map[x][y] == ' ')
		{
			Map[x][y] = 'O';
			break;
		}
	}
    //划归蛇的位置
	Map[snake[0][0]][snake[0][1]] = '@';
	Map[snake[1][0]][snake[1][1]] = '*';
	Map[snake[2][0]][snake[2][1]] = '*';

    //打印地图
	GamePrint(Map, HighestScore, score, hBuffer);
	//等待2秒后开始游戏	
	Sleep(2000);
	//游戏运行过程的循环
    while (1)
	{
        //蛇的变化：将尾巴置为空格，所有部位向前移动一个，覆盖掉原本头的位置
		//保存蛇尾的位置，便于失败后恢复蛇尾以及增加长度
        Map[snake[length-1][0]][snake[length-1][1]] = ' ';
		int tail[2] = {snake[length-1][0], snake[length-1][1]};
        for(int i = length-1; i > 0; i--)
		{
			snake[i][0] = snake[i-1][0];
			snake[i][1] = snake[i-1][1];
		}
        //根据存储的方向确定头的下一个位置
        switch (direction)
        {
        case 1:
            snake[0][0]--;
            break;
        case 2:
            snake[0][1]--;
            break;
        case 3:
            snake[0][0]++;
            break;
        case 4:
            snake[0][1]++;
            break;
        default:
            break;
        }
        //边界限制：N-1行 x N列的网格（第0行到第N-2行有效）
        if(snake[0][0] < 0 || snake[0][0] >= N-1 || snake[0][1] < 0 || snake[0][1] >= N || Map[snake[0][0]][snake[0][1]] == '*')
		{
			//结束界面，暂停界面等都是使用弹窗的形式
			//因此上述函数内部首先都会打印一遍暂停或结束时贪吃蛇的状态
			//然后在中间区域加入弹窗
			//为防止各个弹窗混乱，因此每种情况都使用一个函数
			
			//结束界面
			//结束函数内部由于有重来选项，每次重来都为递归调用游戏函数
			//因此结束函数后面加入return可以保证递归退出过程不会再次进入游戏函数
			EndLogic(Map, HighestScore, score, hBuffer);
			return;
		}
        //检测是否吃到食物
        if (Map[snake[0][0]][snake[0][1]] == 'O')
		{
			//得分增加
			//速度大于15（速度数值越大速度越慢）时，吃到食物得分为1分
			if(speed > 15)
			{
				score+=1;
			}
			//速度小于等于15时，速度数值每减小1，吃到食物的得分加1分
			else
			{
				score+=(1*(16-speed));
			}
			//时刻更新最高分
			*HighestScore = score>*HighestScore?score:*HighestScore;
			//吃到食物后速度增加，最小不低于3
			speed = 3>speed-1?3:speed-1;
			
            //吃到食物后增加蛇的长度，增加的部分即原来的蛇尾
            length++;
            Map[tail[0]][tail[1]] = '*';
            snake[length-1][0] = tail[0]; snake[length-1][1] = tail[1];
			//再次生成食物
			while(1)
			{
				x = rand() % (N-1);
				y = rand() % N;
				if(Map[x][y] == ' ')
				{
					Map[x][y] = 'O';
					break;
				}
			}
		}
		//更新蛇头的位置，将原来的蛇头划归为蛇身
    	Map[snake[0][0]][snake[0][1]] = '@';
		Map[snake[1][0]][snake[1][1]] = '*';
		
		//打印地图
        GamePrint(Map, HighestScore, score, hBuffer);
        //GetKey函数详细在函数内部
        int temp = GetKey(speed, direction);
        //检测Esc键状态
        if(temp == 0)
		{
			//暂停函数返回1，说明继续游戏，暂停2秒后游戏继续
			//暂停函数返回2，说明退出游戏，跳转到结束界面
			int temp1 = PauseLogic(Map, HighestScore, score, hBuffer);
			if(temp1 == 2)
			{
				EndLogic(Map, HighestScore, score, hBuffer);
				return;
			}
			else
			{
				//利用游戏界面覆盖掉暂停界面
				GamePrint(Map, HighestScore, score, hBuffer);
				Sleep(2000);
			}
		}
		//检测加减速
		else if(temp == 5)
		{
			speed = 3>speed-1?3:speed-1;
		}
		else if(temp == 6)
		{
			//减速数值设置为2，便于玩家快速降速
			speed = 20<speed+2?20:speed+2;
		}
		//检测方向，temp只有为1，2，3，4时才会赋值给方向，防止赋值其他数值导致程序异常终止
		else
        	direction = temp;
    }
}

void HScorePrint(char Map[N][N], int* HighestScore, HANDLE hBuffer)
{
    //定义 CHAR_INFO 缓冲区（存储字符 + 颜色）
    CHAR_INFO charBuffer[N + 2][2*N + 2];

    //填充 CHAR_INFO 缓冲区
    //颜色内容：边框：绿色
    //          背景：白色
    //上边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[0][i].Char.AsciiChar = '-';
		charBuffer[0][i].Attributes = FOREGROUND_GREEN;
		
	}
	//地图内容
    for (int i = 0; i < N; i++)
	{
        //左边界
        charBuffer[i+1][0].Char.AsciiChar = '|';
        charBuffer[i+1][0].Attributes = FOREGROUND_GREEN;
		
        //地图内容
        for(int j = 0; j < N; j++)
		{
            charBuffer[i+1][2*j+1].Char.AsciiChar = Map[i][j];
            charBuffer[i+1][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | 
			                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            charBuffer[i+1][2*j+2].Char.AsciiChar = ' ';
            charBuffer[i+1][2*j+2].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
			                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
        }

        char Str[30];
		if(i == 6)
		{
			sprintf(Str, "Highest Score");
			for(int j = N/2 + 3, k = 0; j <= N/2 + 16; j++)
			{
				charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			}
		}
        if(i == 8)
		{
			sprintf(Str, "%d ", *HighestScore);
			for(int j = N - (int)strlen(Str)/2 - 1, k = 0; j <= N + (int)strlen(Str)/2 - 1; j++)
			{
				charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			}
		}
        if(i == 11)
		{
			sprintf(Str, "0.Quit");
			for(int j = N/2 + 6, k = 0; j <= N/2 + 11; j++)
			{
				charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			}
		}


        //右边界
        charBuffer[i+1][2*N+1].Char.AsciiChar = '|';
		charBuffer[i+1][2*N+1].Attributes = FOREGROUND_GREEN;
    }
    //下边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[N+1][i].Char.AsciiChar = '-';
		charBuffer[N+1][i].Attributes = FOREGROUND_GREEN;
	}

    COORD bufferSize = {2*N + 2, N + 2};    //定义写入区域
    COORD bufferCoord = {0, 0};      //从 CHAR_INFO 缓冲区的 (0,0) 开始读取
    SMALL_RECT writeRegion = {0, 0, 2*N + 1, N + 1};   //控制台写入区域

    //使用双缓冲一次性写入字符和颜色
    WriteConsoleOutput(
        hBuffer,    //控制台的屏幕缓冲区（直接输出的缓冲区）
        (CHAR_INFO*)charBuffer,     // CHAR_INFO 缓冲区（预先准备的写入了字符和颜色的缓冲区）
        bufferSize,    //写入的区域
        bufferCoord,    //读取起始点
        &writeRegion    //控制台写入区域
    );

    //重置光标
    SetConsoleCursorPosition(hBuffer, (COORD){0, 0});
}

void HScoreLogic(char Map[N][N], int* HighestScore, HANDLE hBuffer)
{
    //清洗地图
    for(int i = 0; i < N; i++)
	{
		for(int j = 0; j < N; j++)
		{
			Map[i][j] = ' ';
		}
	}

    //打印最高分显示界面
	HScorePrint(Map, HighestScore, hBuffer);
	Sleep(500);
    while (1)
	{
        //检测键盘录入
		if(GetAsyncKeyState('0') & 0x8000)
		{
			break;
		}
        Sleep(100);
    }
}

void SelecPrint(char Map[N][N], HANDLE hBuffer)
{
    //定义 CHAR_INFO 缓冲区（存储字符 + 颜色）
    CHAR_INFO charBuffer[N + 2][2*N + 2];

    //填充 CHAR_INFO 缓冲区
    //颜色内容：边框：绿色
    //          背景：白色
    //          蛇：黄色
    //          食物：红色
    //上边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[0][i].Char.AsciiChar = '-';
		charBuffer[0][i].Attributes = FOREGROUND_GREEN;
		
	}
	//地图内容
    for (int i = 0; i < N; i++)
	{
        //左边界
        charBuffer[i+1][0].Char.AsciiChar = '|';
        charBuffer[i+1][0].Attributes = FOREGROUND_GREEN;
		
        //用空格填充整个界面，并设为白色，充当背景
        for(int j = 0; j < N; j++)
		{
            charBuffer[i+1][2*j+1].Char.AsciiChar = Map[i][j];
            charBuffer[i+1][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | 
			                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            charBuffer[i+1][2*j+2].Char.AsciiChar = ' ';
            charBuffer[i+1][2*j+2].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
			                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
        }
        
        char Str[30];
        //利用sprintf将要打印的字符串赋值给Str
        //然后选取特定的坐标范围，将缓冲区特定位置的空格改为要打印的字符，并赋上颜色
        //4-6行为游戏名字符画
        if(i == 4)
		{
			sprintf(Str, "Greedy****  ");
			for(int j = N/2 + 4, k = 0; j <= N/2 + 15; j++)
			{
				charBuffer[i][j].Char.AsciiChar = Str[k++];
				if(Str[k-1] == '*')
					charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | 
												  BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
				else
					charBuffer[i][j].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			}
		}
		if(i == 5)
		{
			sprintf(Str, "  *****Sn@ke");
			for(int j = N/2 + 4, k = 0; j <= N/2 + 15; j++)
			{
				charBuffer[i][j].Char.AsciiChar = Str[k++];
				if(Str[k-1] == '*' ||Str[k-1] == '@')
					charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | 
												  BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
				else
					charBuffer[i][j].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			}
		}
		if(i == 6)
		{
			sprintf(Str, " **     (O_^)");
			for(int j = N/2 + 4, k = 0; j <= N/2 + 16; j++)
			{
				charBuffer[i][j].Char.AsciiChar = Str[k++];
				if(Str[k-1] == '*')
					charBuffer[i][j].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | 
												  BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
				else if(Str[k-1] == 'O')
					charBuffer[i][j].Attributes = FOREGROUND_RED | 
												  BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
				else
					charBuffer[i][j].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			}
		}
        //9-11行为各个选项
        if(i == 9)
		{
			sprintf(Str, "1.Start");
			for(int j = N/2 + 2, k = 0; j <= N/2 + 8; j++)
			{
				charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			}
		}
		
        if(i == 10)
		{
			sprintf(Str, "2.Highest Score");
			for(int j = N/2 + 2, k = 0; j <= N/2 + 16; j++)
			{
				charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			}
		}
		
        if(i == 11)
		{
			sprintf(Str, "0.Exit");
			for(int j = N/2 + 2, k = 0; j <= N/2 + 7; j++)
			{
				charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			}
		}
        //提示
        if(i == 15)
		{
			sprintf(Str, "Enter a number for option!");
			for(int j = 9, k = 0; j <= 34; j++)
			{
				charBuffer[i][j].Char.AsciiChar = Str[k++];
				charBuffer[i][j].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			}
		}

        //右边界
        charBuffer[i+1][2*N+1].Char.AsciiChar = '|';
		charBuffer[i+1][2*N+1].Attributes = FOREGROUND_GREEN;
    }
    //下边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[N+1][i].Char.AsciiChar = '-';
		charBuffer[N+1][i].Attributes = FOREGROUND_GREEN;
	}

    COORD bufferSize = {2*N + 2, N + 2};    //定义写入区域
    COORD bufferCoord = {0, 0};      //从 CHAR_INFO 缓冲区的 (0,0) 开始读取
    SMALL_RECT writeRegion = {0, 0, 2*N + 1, N + 1};   //控制台写入区域

    //使用双缓冲一次性写入字符和颜色
    WriteConsoleOutput(
        hBuffer,    //控制台的屏幕缓冲区（直接输出的缓冲区）
        (CHAR_INFO*)charBuffer,     // CHAR_INFO 缓冲区（预先准备的写入了字符和颜色的缓冲区）
        bufferSize,    //写入的区域
        bufferCoord,    //读取起始点
        &writeRegion    //控制台写入区域
    );

    //重置光标
    SetConsoleCursorPosition(hBuffer, (COORD){0, 0});
}

void SelecLogic(char Map[N][N], int* HighestScore, HANDLE hBuffer)
{
    //清洗地图
    for(int i = 0; i < N; i++)
	{
		for(int j = 0; j < N; j++)
		{
			Map[i][j] = ' ';
		}
	}
	
    //打印选择界面
	SelecPrint(Map, hBuffer);
	Sleep(500);
	
    while (1)
	{
		if(GetAsyncKeyState('1') & 0x8000)
		{
			//进入游戏
			GameLogic(Map, HighestScore, hBuffer);
			//缓冲0.5秒，防止运行过快读取遗留键盘录入造成误触
			Sleep(500);
		}
		if(GetAsyncKeyState('2') & 0x8000)
		{
			//进入最高分显示界面
			HScoreLogic(Map, HighestScore, hBuffer);
			Sleep(500);
		}
		if(GetAsyncKeyState('0') & 0x8000)
		{
			//结束函数，即结束游戏
			break;
		}
		
		//此处再次打印地图可以保证从各个选项返回后选择界面可以正常显示
        SelecPrint(Map, hBuffer);
        Sleep(100);
    }
}

void BeginPrint(char Map[N][N], HANDLE hBuffer)
{
    //定义 CHAR_INFO 缓冲区（存储字符 + 颜色）
    CHAR_INFO charBuffer[N + 2][2*N + 2];

    //填充 CHAR_INFO 缓冲区
    //颜色内容：边框：绿色
    //          背景：白色
    //          蛇：黄色
    //          食物：红色
    //上边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[0][i].Char.AsciiChar = '-';
		charBuffer[0][i].Attributes = FOREGROUND_GREEN;
		
	}
	//地图内容
    for (int i = 0; i < N; i++)
	{
        //左边界
        charBuffer[i+1][0].Char.AsciiChar = '|';
        charBuffer[i+1][0].Attributes = FOREGROUND_GREEN;
		
        //地图内容
        for(int j = 0; j < N; j++)
		{
            charBuffer[i+1][2*j+1].Char.AsciiChar = Map[i][j];
            if (Map[i][j] == '@' || Map[i][j] == '*')
                charBuffer[i+1][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else if (Map[i][j] == 'O')
                charBuffer[i+1][2*j+1].Attributes = FOREGROUND_RED | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            else
            	charBuffer[i+1][2*j+1].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | 
				                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
            charBuffer[i+1][2*j+2].Char.AsciiChar = ' ';
            charBuffer[i+1][2*j+2].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
			                                    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
        }
        //标语
		if(i == 9)
		{
			charBuffer[i+1][N/2 + 3].Char.AsciiChar = 'W';
			charBuffer[i+1][N/2 + 3].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			charBuffer[i+1][N/2 + 5].Char.AsciiChar = 'e';
			charBuffer[i+1][N/2 + 5].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			charBuffer[i+1][N/2 + 7].Char.AsciiChar = 'l';
			charBuffer[i+1][N/2 + 7].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			charBuffer[i+1][N/2 + 9].Char.AsciiChar = 'c';
			charBuffer[i+1][N/2 + 9].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			//o在地图中有，不做处理
			charBuffer[i+1][N/2 + 13].Char.AsciiChar = 'm';
			charBuffer[i+1][N/2 + 13].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
			charBuffer[i+1][N/2 + 15].Char.AsciiChar = 'e';
			charBuffer[i+1][N/2 + 15].Attributes = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
		}
        //右边界
        charBuffer[i+1][2*N+1].Char.AsciiChar = '|';
		charBuffer[i+1][2*N+1].Attributes = FOREGROUND_GREEN;
    }
    //下边界
	for(int i = 0; i < 2*N+2; i++)
	{
		charBuffer[N+1][i].Char.AsciiChar = '-';
		charBuffer[N+1][i].Attributes = FOREGROUND_GREEN;
	}

    COORD bufferSize = {2*N + 2, N + 2};    //定义写入区域
    COORD bufferCoord = {0, 0};      //从 CHAR_INFO 缓冲区的 (0,0) 开始读取
    SMALL_RECT writeRegion = {0, 0, 2*N + 1, N + 1};   //控制台写入区域

    //使用双缓冲一次性写入字符和颜色
    WriteConsoleOutput(
        hBuffer,    //控制台的屏幕缓冲区（直接输出的缓冲区）
        (CHAR_INFO*)charBuffer,     // CHAR_INFO 缓冲区（预先准备的写入了字符和颜色的缓冲区）
        bufferSize,    //写入的区域
        bufferCoord,    //读取起始点
        &writeRegion    //控制台写入区域
    );

    //重置光标
    SetConsoleCursorPosition(hBuffer, (COORD){0, 0});
}

void BeginLogic(char Map[N][N], HANDLE hBuffer)
{
    //定义蛇的长度及所有部位的坐标
    int length = N/2, snake[N*N][2];
	//定义初始蛇各个部位的坐标
    for(int i = 0; i < N/2; i++)
    {
        snake[i][0] = N/4 + 2; snake[i][1] = N/4 + i;
        Map[snake[i][0]][snake[i][1]] = '*';
    }
    //定义食物的坐标
    int x = N/2 - 1, y = N/2, direction = 2;
    Map[x][y] = 'O';
    //划归蛇头的位置
	Map[snake[0][0]][snake[0][1]] = '@';
	
    //打印地图
	BeginPrint(Map, hBuffer);
	Sleep(500);
    while (1)
	{
        //蛇的变化：将尾巴置为空格，所有部位向前移动一个，覆盖掉原本头的位置
        Map[snake[length-1][0]][snake[length-1][1]] = ' ';
        for(int i = length-1; i > 0; i--)
		{
			snake[i][0] = snake[i-1][0];
			snake[i][1] = snake[i-1][1];
		}
        //根据存储的方向确定头的下一个位置
        switch (direction)
        {
        case 1:
            snake[0][0]--;
            break;
        case 2:
            snake[0][1]--;
            break;
        case 3:
            snake[0][0]++;
            break;
        case 4:
            snake[0][1]++;
            break;
        default:
            break;
        }
        //检测是否吃到食物
        if (Map[snake[0][0]][snake[0][1]] == 'O')
		{
            //初始界面1
            Map[snake[0][0]][snake[0][1]] = '@';
			Map[snake[1][0]][snake[1][1]] = '*';
			BeginPrint(Map, hBuffer);
            Sleep(1500);
            return;
		}
		//重新划归蛇的位置,划归下一个蛇头，以及将接替蛇头的位置的蛇头划归为蛇身
    	Map[snake[0][0]][snake[0][1]] = '@';
		Map[snake[1][0]][snake[1][1]] = '*';
		//打印地图
        BeginPrint(Map, hBuffer);
        //按照规定改变方向
        if(snake[0][0] == N/4 + 2  && snake[0][1] == N/4 - 1)
            direction = 3;
        if(snake[0][0] == 3*N/4 - 4 && snake[0][1] == N/4 - 1)
            direction = 4;
        if(snake[0][0] == 3*N/4 - 4 && snake[0][1] == N/2)
            direction = 1;
        Sleep(150);
    }
}

void HelpPrint()
{
	//上边界
	for(int i = 0; i < 2*N+2; i++)
	    printf("-");
	printf("\n");
	//打印地图
    for(int i = 0; i < N; i++)
	{
		//左边界
		printf("|");
        if(i == 0)
        {
            printf("              游戏说明                  |\n");
            continue;
        }
		if(i == 2)
		{
			printf("    贪吃蛇游戏是一款经典的小游戏，你需要|\n");
			continue;
		}
		if(i == 3)
		{
			printf("控制一条不断移动的小蛇，吃掉屏幕上出现的|\n");
			continue;
		}
		if(i == 4)
		{
			printf("食物。每吃掉一个食物，蛇的身体就会变长。|\n");
			continue;
		}
		if(i == 5)
		{ 
			printf("游戏的目标是尽可能长时间地生存下去，同时|\n");
			continue; 
		}
		if(i == 6)
		{ 
			printf("避免蛇头撞到自己的身体或屏幕边缘。      |\n");
			continue; 
		}
		if(i == 8)
		{ 
			printf("              操作说明                  |\n");
			continue; 
		}
		if(i == 10)
		{ 
			printf(" 控制:W,A,S,D移动;F1加速,F2减速,ESC暂停 |\n");
			continue; 
		}
		if(i == 12)
		{ 
			printf("              小 贴 士                  |\n");
			continue; 
		}
		if(i == 14)
		{ 
			printf("  小蛇吃到食物或者按下F1时，小蛇会加速，|\n");
			continue; 
		}
		if(i == 15)
		{ 
			printf("加速后吃食物得分会更高。如果你觉得太快，|\n");
			continue; 
		}
		if(i == 16)
		{ 
			printf("也可以按下F2减速。祝你游戏愉快！        |\n");
			continue; 
		}
		if(i == 19)
		{ 
			printf("             按下ESC键返回              |\n");
			continue; 
		}												
		for(int j = 0; j < N; j++)
		{
			printf("  ");
		}
		//右边界
		printf("|");
		printf("\n");
	}
	//下边界
	for(int i = 0; i < 2*N+2; i++)
	    printf("-");
	printf("\n");
	
	//获取ESC键后退出
	while(1)
	{
		if (GetAsyncKeyState(VK_ESCAPE) & 0x8000)
	    {
	  	 	break;
        }		
	}
}

void StartPrint()
{
	//上边界
	for(int i = 0; i < 2*N+2; i++)
	    printf("-");
	printf("\n");
	//打印地图
    for(int i = 0; i < N; i++)
	{
		//左边界
		printf("|");

		if(i == 7)
		{
			printf("           按下F以进入游戏              |\n");
			continue;
		}

		if(i == 11)
		{
			printf("           按下H以进入介绍              |\n");
			continue;
		}
		
		if(i == 12)
		{
			printf("       (进入游戏后不可再查看)           |\n");
			continue;
		}

		for(int j = 0; j < N; j++)
		{
			printf("  ");
		}
		//右边界
		printf("|");
		printf("\n");
	}
	//下边界
	for(int i = 0; i < 2*N+2; i++)
	    printf("-");
	printf("\n");
	//循环获取键盘输入，以进入对应选项
	while(1)
	{
		if (GetAsyncKeyState('F') & 0x8000)
	    {
	    	//结束此函数后，即进入游戏主题
	  	 	break;
        }
        if (GetAsyncKeyState('H') & 0x8000)
	    {
	    	//清屏后进入帮助函数
	    	ClearScreen();
	  	 	HelpPrint();
	  	 	//帮助函数的返回界面仍为开始界面，递归可以简化一些操作
	  	 	ClearScreen();
	  	 	StartPrint();
	  	 	//保证开始界面的递归结束后可以直接结束，避免再次进入循环
	  	 	break;
        }
        Sleep(100);
	}
}

void ExplainPrint()
{
	//上边界
	for(int i = 0; i < 2*N+2; i++)
	    printf("-");
	printf("\n");
	//打印地图
    for(int i = 0; i < N; i++)
	{
		//左边界
		printf("|");
		//在特定行打印文字
        if(i == 5)
        {
            printf("              游戏前详阅                |\n");
            continue;
        }
		if(i == 7)
		{
			printf("     请先调整游戏窗口至适宜的大小       |\n");
			continue;
		}
		if(i == 9)
		{
			printf("        游戏内勿随意调整窗口大小        |\n");
			continue;
		}
		if(i == 11)
		{
			printf("     请将键盘设置为大写锁定或英文       |\n");
			continue;
		}
		if(i == 13)
		{
			printf("    按下选项前对应的数字以进入该选项    |\n");
			continue;
		}
		if(i == 15)
		{
			printf("          按回车键以开始游戏            |\n");
			continue;
		}
		//其余行以空格填充
		for(int j = 0; j < N; j++)
		{
			printf("  ");
		}
		//右边界
		printf("|\n");
	}
	//下边界
	for(int i = 0; i < 2*N+2; i++)
	    printf("-");
	printf("\n");
	//获取回车键后结束函数
	getchar();
}
